/*
 *  Copyright (c) 2011 Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the GNU Lesser Public License v2.1
 *  which accompanies this distribution, and is available at
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *  Contributors:
 *      Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany - RMI Client, FileChooser and WebDAV
 */
package de.ipk_gatersleben.bit.bi.edal.webdav.wrapper.primary_data;

import java.io.InputStream;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBException;

import io.milton.resource.CollectionResource;
import io.milton.resource.DeletableCollectionResource;
import io.milton.resource.MakeCollectionableResource;
import io.milton.resource.MoveableResource;
import io.milton.resource.PropFindableResource;
import io.milton.resource.PutableResource;
import io.milton.resource.Resource;
import io.milton.http.Auth;
import io.milton.http.Request;
import io.milton.http.Request.Method;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.ConflictException;
import io.milton.http.exceptions.NotAuthorizedException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataDirectoryException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataEntityVersionException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataFileException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.metadata.MetaData;
import de.ipk_gatersleben.bit.bi.edal.primary_data.metadata.MetaDataException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.security.EdalAuthenticateException;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.ClientDataManager;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.ClientPrimaryDataDirectory;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.ClientPrimaryDataEntity;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.ClientPrimaryDataFile;
import de.ipk_gatersleben.bit.bi.edal.webdav.wrapper.FileSystemHandler;
import de.ipk_gatersleben.bit.bi.edal.webdav.wrapper.metadata.ExtXMLConfig;
import de.ipk_gatersleben.bit.bi.edal.webdav.wrapper.metadata.Property;

/**
 * A resource representing a PrimaryDataDirectory.
 * 
 * @author benz, arendd
 * 
 */

public class PrimaryDataDirectoryResource extends PrimaryDataEntityResource
		implements CollectionResource, PutableResource,
		MakeCollectionableResource, DeletableCollectionResource,
		MoveableResource {
	private ClientPrimaryDataDirectory dir;

	public PrimaryDataDirectoryResource(String path,
			FileSystemHandler fileSystemHandler) throws RemoteException,
			PrimaryDataDirectoryException, NotBoundException,
			EdalAuthenticateException {
		super(path, fileSystemHandler);
		if (entity != null) {
			dir = (ClientPrimaryDataDirectory) entity;
		}
	}

	public ClientPrimaryDataDirectory getDir() {
		return this.dir;
	}

	@Override
	public boolean authorise(Request request, Method method, Auth auth) {
		boolean b = super.authorise(request, method, auth);
		if (entity != null) {
			dir = (ClientPrimaryDataDirectory) entity;
		}
		return b;
	}

	@Override
	public Resource child(String childName) {
		try {
			if (this.dir.exist(childName)) {
				PrimaryDataEntityResource child = new PrimaryDataEntityResource(
						this.dir.getPrimaryDataEntity(childName).getPath(),
						fileSystemHandler);
				if (child.isDirectory()) {
					return (PrimaryDataDirectoryResource) child;
				} else {
					// files inside a directory are either
					Pattern property = Pattern
							.compile(".*\\.v[0-9]+\\.?[^\\.]*\\.properties\\.xml$");
					Matcher pm = property.matcher(childName);
					// virtual files
					if (!pm.matches()) {
						return new PrimaryDataFileDirectoryVirtualResource(
								entity.getPath(), fileSystemHandler);
						// or property files
					} else {
						return new PrimaryDataEntityPropertyVirtualResource(
								entity.getCurrentVersion(), entity
										.getCurrentVersion().getRevision());
					}
				}
			}
		} catch (RemoteException e) {
			ClientDataManager.logger.error("Failed to get resource for: "
					+ childName + "\nReason: " + e.toString());
		} catch (PrimaryDataDirectoryException e) {
			ClientDataManager.logger.error("Failed to get resource for: "
					+ childName + "\nReason: " + e.toString());
		} catch (JAXBException e) {
			ClientDataManager.logger.error("Failed to get resource for: "
					+ childName + "\nReason: " + e.toString());
		} catch (MetaDataException e) {
			ClientDataManager.logger.error("Failed to get resource for: "
					+ childName + "\nReason: " + e.toString());
		} catch (NotBoundException e) {
			ClientDataManager.logger.error("Failed to get resource for: "
					+ childName + "\nReason: " + e.toString());
		} catch (EdalAuthenticateException e) {
			ClientDataManager.logger.error("Failed to get resource for: "
					+ childName + "\nReason: " + e.toString());
		}

		return null;
	}

	@Override
	public List<? extends Resource> getChildren() {
		List<PropFindableResource> children = new ArrayList<PropFindableResource>();
		try {
			List<ClientPrimaryDataEntity> lis = dir.listPrimaryDataEntities();
			for (ClientPrimaryDataEntity ent : lis) {
				if (ent.isDirectory()) {
					if (!ent.getCurrentVersion().isDeleted()) {
						children.add(new PrimaryDataDirectoryResource(ent
								.getPath(), fileSystemHandler));
						/*
						 * children.add(new
						 * PrimaryDataEntityPropertyVirtualResource(
						 * ent.getCurrentVersion(), ent
						 * .getCurrentVersion().getRevision()));
						 */
					}
				} else {
					if (!ent.getCurrentVersion().isDeleted()) {
						children.add(new PrimaryDataFileResource(ent.getPath(),
								fileSystemHandler));
					}
				}
			}
			children.add(new PrimaryDataVersionDirectoryVirtualResource(dir
					.getPath(), fileSystemHandler));
		} catch (RemoteException e) {
			ClientDataManager.logger.error("Failed to get children of: " + dir
					+ "\n" + e.toString());
		} catch (PrimaryDataDirectoryException e) {
			ClientDataManager.logger.error("Failed to get children of: " + dir
					+ "\n" + e.toString());
		}
		/*
		 * catch (JAXBException e) {
		 * ClientDataManager.logger.error("Failed to create properties.xml of: "
		 * + dir + "\n" + e.toString()); } catch (MetaDataException e) {
		 * ClientDataManager.logger.error("Failed to create properties.xml of: "
		 * + dir + "\n: " + e.toString()); }
		 */
		catch (NotBoundException e) {
			ClientDataManager.logger.error("Failed to get children of: " + dir
					+ "\n" + e.toString());
		} catch (EdalAuthenticateException e) {
			ClientDataManager.logger.error("Failed to get children of: " + dir
					+ "\n" + e.toString());
		}

		return children;
	}

	@Override
	// TODO request refresh after file creation
	public Resource createNew(String newName, InputStream inputStream,
			Long length, String contentType) {
		ClientPrimaryDataFile file = null;
		if (!newName.contains("properties")) {
			try {

				if (this.dir.exist(newName)) {
					ClientPrimaryDataEntity ent = this.dir
							.getPrimaryDataEntity(newName);
					if (!ent.isDirectory()) {
						file = (ClientPrimaryDataFile) ent;
						file.store(inputStream);
					}
				} else {
					file = dir.createPrimaryDataFile(newName);
					/*
					 * MetaData newMetaData = file.getMetaData().clone(); Person
					 * newperson = new Person(username,username,"","","");
					 * newMetaData
					 * .setElementValue(EnumDublinCoreElements.CREATOR,
					 * newperson); file.setMetaData(newMetaData);
					 */

					if (length > 0L) {
						file.store(inputStream);
					}
				}
				if (file != null) {
					fileSystemHandler.removecache(file.getPath());
				}
				return new PrimaryDataFileDirectoryVirtualResource(
						file.getPath(), fileSystemHandler);
			} catch (PrimaryDataDirectoryException e) {
				ClientDataManager.logger.error("Failed to create/save '"
						+ newName + "\nReason: " + e.toString());
			} catch (PrimaryDataFileException e) {
				ClientDataManager.logger.error("Failed to create/save file: "
						+ newName + "\nReason: " + e.toString());
			} catch (PrimaryDataEntityVersionException e) {
				ClientDataManager.logger.error("Failed to create/save file: "
						+ newName + "\nReason: " + e.toString());
			} catch (NotBoundException e) {
				ClientDataManager.logger.error("Failed to create/save file: "
						+ newName + "\nReason: " + e.toString());
			} catch (EdalAuthenticateException e) {
				ClientDataManager.logger.error("Failed to create/save file: "
						+ newName + "\nReason: " + e.toString());
			} catch (RemoteException e) {
				ClientDataManager.logger.error("Failed to create/save file: "
						+ newName + "\nReason: " + e.toString());
			}
			/*
			 * catch (MetaDataException e) {
			 * ClientDataManager.logger.error("Failed to create/save file: " +
			 * newName + "\nReason: " + e.toString()); } catch
			 * (CloneNotSupportedException e) {
			 * ClientDataManager.logger.error("Failed to save properties-file: "
			 * + newName + "\nReason: " + e.toString()); }
			 */
		} else {
			// TODO save changes in properties.xml
			ExtXMLConfig conf;
			try {
				conf = new ExtXMLConfig(Property.class);
				Object rawData = conf.load(inputStream);
				if (rawData != null) {
					Property metaData = (Property) rawData;
					Pattern property = Pattern
							.compile("(.*)\\.v[0-9]+\\.?[^\\.]*\\.(properties?\\.?xml|.*)$");
					Matcher pm = property.matcher(newName);
					if (pm.matches()) {
						ClientPrimaryDataEntity dirfile = fileSystemHandler
								.getEntity(dir.getPath() + "/" + pm.group(1));
						if (dirfile != null) {
							MetaData newMetaData = dirfile.getMetaData()
									.clone();
							metaData.updateMetaData(newMetaData);
							dirfile.setMetaData(newMetaData);

							PrimaryDataEntityPropertyVirtualResource virtualxmlresouce = new PrimaryDataEntityPropertyVirtualResource(
									dir.getPath() + "/" + newName,
									fileSystemHandler);
							return virtualxmlresouce;
						}
					}

				}
			} catch (JAXBException e) {
				ClientDataManager.logger
						.error("Failed to save properties-file: " + newName
								+ "\nReason: " + e.toString());
			} catch (RemoteException e) {
				ClientDataManager.logger
						.error("Failed to save properties-file: " + newName
								+ "\nReason: " + e.toString());
			} catch (CloneNotSupportedException e) {
				ClientDataManager.logger
						.error("Failed to save properties-file: " + newName
								+ "\nReason: " + e.toString());
			} catch (PrimaryDataEntityVersionException e) {
				ClientDataManager.logger
						.error("Failed to save properties-file: " + newName
								+ "\nReason: " + e.toString());
			} catch (MetaDataException e) {
				ClientDataManager.logger
						.error("Failed to save properties-file: " + newName
								+ "\nReason: " + e.toString());
			} catch (PrimaryDataDirectoryException e) {
				ClientDataManager.logger
						.error("Failed to save properties-file: " + newName
								+ "\nReason: " + e.toString());
			} catch (NotBoundException e) {
				ClientDataManager.logger.error("Failed to create/save file: "
						+ newName + "\nReason: " + e.toString());
			} catch (EdalAuthenticateException e) {
				ClientDataManager.logger.error("Failed to create/save file: "
						+ newName + "\nReason: " + e.toString());
			}
		}

		return null;
	}

	/**
	 * To create a new Folder by right click -> new folder
	 */
	@Override
	public CollectionResource createCollection(String newName)
			throws NotAuthorizedException, ConflictException,
			BadRequestException {

		ClientPrimaryDataDirectory newDir = null;
		try {
			if (!dir.exist(newName)) {
				newDir = dir.createPrimaryDataDirectory(newName);
				/*
				 * MetaData newMetaData = newDir.getMetaData().clone(); Person
				 * newperson = new Person(username,username,"","","");
				 * newMetaData.setElementValue(EnumDublinCoreElements.CREATOR,
				 * newperson); newDir.setMetaData(newMetaData);
				 */
			}

			return new PrimaryDataDirectoryResource(newDir.getPath(),
					fileSystemHandler);

		} catch (RemoteException e) {
			ClientDataManager.logger.error("Failed to create '" + newName
					+ "'\n" + e.toString());
		} catch (PrimaryDataDirectoryException e) {
			ClientDataManager.logger.error("Failed to create '" + newName
					+ "'\n" + e.toString());
		} catch (NotBoundException e) {
			ClientDataManager.logger.error("Failed to create '" + newName
					+ "'\n" + e.toString());
		} catch (EdalAuthenticateException e) {
			ClientDataManager.logger.error("Failed to create '" + newName
					+ "'\n" + e.toString());
		}
		/*
		 * catch (MetaDataException e) {
		 * ClientDataManager.logger.error("Failed to create '" + newName + "'\n"
		 * + e.toString()); } catch (CloneNotSupportedException e) {
		 * ClientDataManager.logger.error("Failed to create '" + newName + "'\n"
		 * + e.toString()); } catch (PrimaryDataEntityVersionException e) {
		 * ClientDataManager.logger.error("Failed to create '" + newName + "'\n"
		 * + e.toString()); }
		 */

		return null;
	}

	@Override
	public void delete() throws NotAuthorizedException, ConflictException,
			BadRequestException {
		try {
			fileSystemHandler.removecache(dir.getPath());
			this.dir.delete();
		} catch (RemoteException e) {
			ClientDataManager.logger.error("Failed to delete directory: "
					+ e.toString());
		} catch (PrimaryDataEntityVersionException e) {
			ClientDataManager.logger.error("Failed to delete directory: "
					+ e.toString());
		} catch (PrimaryDataDirectoryException e) {
			ClientDataManager.logger.error("Failed to delete directory: "
					+ e.toString());
		}
	}

	@Override
	public void moveTo(CollectionResource rDest, String name)
			throws ConflictException, NotAuthorizedException,
			BadRequestException {
		try {
			fileSystemHandler.removecache(dir.getPath());
			PrimaryDataDirectoryResource dir = (PrimaryDataDirectoryResource) rDest;

			if (dir != null) {
				ClientPrimaryDataDirectory oldParent = this.dir
						.getParentDirectory();
				ClientPrimaryDataDirectory newParent = dir.getDir();

				if (oldParent.getPath().compareTo(newParent.getPath()) == 0
						&& !oldParent.exist(name)) {
					this.dir.rename(name);
				} else {
					if (oldParent != null) {
						this.dir.move(newParent);
					}
				}

				// this.dir.switchCurrentVersion(this.dir.getVersions().last());
			}
		} catch (RemoteException e) {
			ClientDataManager.logger.error("Failed to move/rename File: "
					+ e.toString());
		} catch (PrimaryDataDirectoryException e) {
			ClientDataManager.logger.error("Failed to move/rename File: "
					+ e.toString());
		} catch (PrimaryDataEntityVersionException e) {
			ClientDataManager.logger.error("Failed to rename File: "
					+ e.toString());
		}
	}

	@Override
	public boolean isLockedOutRecursive(Request request) {
		return false;
	}

}
